﻿using static QdArch.SlabAction;
using Shape = LightCAD.Three.Shape;

namespace QdArch
{
    public  class Slab3dAction: ComponentInstance3dAction
    {
        public override List<Object3D> Render(IComponentInstance cptIns)
        {
            var slab = cptIns as QdSlab;
            var slabDef = slab.Definition as QdSlabDef;

            var mats = slabDef.Solid3dProvider.AssignMaterialsFunc(cptIns,null);
            var curMat = mats.Select(m => RenderMaterialManager.GetRenderMaterial(m.Uuid)).ToArray();

            var outPoints = slab.Outline.ToListEx();
            var start = slab.Outline[0].ToThree(slab.Bottom);
            var end = slab.Outline[1].ToThree(slab.Bottom);
            var lineVec = new Vector3().SubVectors(end, start);
            var xAxis = VectorUtil.XAxis;
            var yAxis = VectorUtil.YAxis;
            var zAxis = VectorUtil.ZAxis;
            var coodMat = new Matrix4();
            //coodMat.makeBasis(xAxis, yAxis, zAxis);

            var holes = new ListEx<Path>();
            var upSlabMeshs = new ListEx<Mesh>();
            var downSlabMeshs = new ListEx<Mesh>();

            foreach (var innerPoints in slab.Holes)
            {
                holes.Add(new Path(innerPoints.ToListEx()));
            }

            foreach (var upSlab in slab.UpSlabs)
            {
                upSlab.Thickness = slab.Thickness;
                holes.Add(new Path(upSlab.Outline.ToListEx()));
                var upSlabGeos = CreateEmbedSlab(EmbedType.UpSlab, upSlab, coodMat);
                foreach (var upSlabGeo in upSlabGeos)
                {
                    var upSlabMesh = new Mesh(upSlabGeo, curMat);
                    upSlabMeshs.Add(upSlabMesh);
                }
            }

            foreach (var downSlab in slab.DownSlabs)
            {
                downSlab.Thickness = slab.Thickness;
                holes.Add(new Path(downSlab.Outline.ToListEx()));
                var downSlabGeos = CreateEmbedSlab(EmbedType.DownSlab, downSlab, coodMat);
                foreach (var downSlabGeo in downSlabGeos)
                {
                    var downSlabMesh = new Mesh(downSlabGeo, curMat);
                    downSlabMeshs.Add(downSlabMesh);
                }
            }

            if (ShapeUtils.isClockWise(outPoints))
            {
                outPoints.Reverse();
            }
            var shape = new Shape(outPoints)
            {
                holes = holes
            };
            double thicknessHlf = slab.Thickness / 2;
            var geoData = GeoUtil.GetStretchGeometryData(shape, coodMat, slab.Thickness, 0);
            var geo = geoData.GetBufferGeometry();
            geo.computeVertexNormals();

            var wallMesh = new Mesh(geo, curMat);

            var result = new ListEx<Object3D>();
            result.Add(wallMesh);
            result.AddRange(upSlabMeshs);
            result.AddRange(downSlabMeshs);

            return result;
        }

        public BufferGeometry[] CreateEmbedSlab(EmbedType embedType, QdUpDownSlab embedSlab, Matrix4 coodMat)
        {
            double bottom = embedSlab.Bottom + embedSlab.Thickness;
            double top = bottom + embedSlab.Thickness;

            double ringBottom = embedSlab.Thickness;
            double ringTop = bottom;

            if (embedType == EmbedType.DownSlab)
            {
                bottom = -bottom - embedSlab.Thickness * 2;
                top = bottom + embedSlab.Thickness;

                ringTop = 0;
                ringBottom = top;
            }
            var outPoints = embedSlab.Outline.ToListEx();
            if (ShapeUtils.isClockWise(outPoints))
            {
                outPoints.Reverse();
            }

            var ringOutPoints = ShapeUtil.ComputeRingPoints(outPoints.Clone(), -embedSlab.Thickness, true);
            if (ShapeUtils.isClockWise(ringOutPoints))
            {
                ringOutPoints.Reverse();
            }

            var shape = new Shape(ringOutPoints);
            var slabGeoData = GeoUtil.GetStretchGeometryData(shape, coodMat, top, bottom);
            var slabGeo = slabGeoData.GetBufferGeometry();
            slabGeo.computeVertexNormals();

            var ring = new Shape(ringOutPoints)
            {
                holes = new ListEx<Path> { new Path(outPoints) }
            };

            var ringGeoData = GeoUtil.GetStretchGeometryData(ring, coodMat, ringTop, ringBottom);
            var ringGeo = ringGeoData.GetBufferGeometry();
            ringGeo.computeVertexNormals();

            return new BufferGeometry[] { slabGeo, ringGeo };
        }
    }
}